﻿using System.Collections;

namespace KinonekoSoftware.Studio.Collections
{
    /// <summary>
    /// <see cref="DictionaryAdapter{TKey,TValue}"/> 类型可以用作 <see cref="Dictionary{TKey,TValue}"/> 类型的适配器，并在此之上提供有限的<see cref="IReadOnlySet{T}"/> 功能。
    /// </summary>
    /// <typeparam name="TKey"></typeparam>
    /// <typeparam name="TValue"></typeparam>
    public sealed class DictionaryAdapter<TKey, TValue> : IDictionary<TKey, TValue>, IReadOnlySet<TKey>
    {
        private readonly Dictionary<TKey, TValue> _impl;
        private readonly HashSet<TKey>            _set;

        #region Constructor

        public DictionaryAdapter()
        {
            _impl = new Dictionary<TKey, TValue>();
            _set  = new HashSet<TKey>();
        }

        public DictionaryAdapter(int capacity)
        {
            _impl = new Dictionary<TKey, TValue>(capacity);
            _set  = new HashSet<TKey>(capacity);
        }

        public DictionaryAdapter(IEqualityComparer<TKey> comparer)
        {
            _impl = new Dictionary<TKey, TValue>(comparer);
            _set  = new HashSet<TKey>(comparer);
        }

        public DictionaryAdapter(int capacity, IEqualityComparer<TKey> comparer)
        {
            _impl = new Dictionary<TKey, TValue>(capacity, comparer);
            _set  = new HashSet<TKey>(capacity, comparer);
        }

        public DictionaryAdapter(IDictionary<TKey, TValue> dictionary)
        {
            _impl = new Dictionary<TKey, TValue>(dictionary, null);
            _set  = new HashSet<TKey>(dictionary.Select(x => x.Key), null);
        }

        #endregion

        #region Add

        public void Add(TKey key, TValue value)
        {
            _impl.Add(key, value);
            _set.Add(key);
        }

        public void Add(KeyValuePair<TKey, TValue> item)
        {
            ((IDictionary<TKey, TValue>)_impl).Add(item);
            _set.Add(item.Key);
        }

        #endregion

        #region CopyTo

        public void CopyTo(TKey[] array, int arrayIndex) => _impl.Keys.CopyTo(array, arrayIndex);

        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) => ((IDictionary<TKey, TValue>)_impl).CopyTo(array, arrayIndex);

        #endregion

        #region Contains

        public bool Contains(TKey item) => _set.Contains(item);


        public bool Contains(KeyValuePair<TKey, TValue> item) => ((IDictionary<TKey, TValue>)_impl).Contains(item);

        public bool ContainsKey(TKey key) => _impl.ContainsKey(key);

        #endregion

        #region GetEnumerator

        IEnumerator<TKey> IEnumerable<TKey>.GetEnumerator() => Keys.GetEnumerator();

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() => _impl.GetEnumerator();

        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();

        #endregion

        #region ISet Impls

        public bool IsProperSubsetOf(IEnumerable<TKey> other) => _set.IsProperSubsetOf(other);

        public bool IsProperSupersetOf(IEnumerable<TKey> other) => _set.IsProperSupersetOf(other);

        public bool IsSubsetOf(IEnumerable<TKey> other) => _set.IsSubsetOf(other);

        public bool IsSupersetOf(IEnumerable<TKey> other) => _set.IsSupersetOf(other);

        public bool Overlaps(IEnumerable<TKey> other) => _set.Overlaps(other);

        public bool SetEquals(IEnumerable<TKey> other) => _set.SetEquals(other);

        #endregion

        #region Remove

        public bool Remove(KeyValuePair<TKey, TValue> item)
        {
            return ((IDictionary<TKey, TValue>)_impl).Remove(item) && _set.Remove(item.Key);
        }

        public bool Remove(TKey key) => _impl.Remove(key) && _set.Remove(key);

        #endregion

        public bool TryGetValue(TKey key, out TValue value) => _impl.TryGetValue(key, out value);

        public TValue GetValueOrDefault(TKey key) => !_impl.TryGetValue(key, out var obj) ? default(TValue) : obj;

        public bool TryAdd(TKey key, TValue value)
        {
            return _impl.TryAdd(key, value) && _set.Add(key);
        }

        public void Clear()
        {
            _impl.Clear();
            _set.Clear();
        }


        public int  Count      => _impl.Count;
        public bool IsReadOnly => ((IDictionary<TKey, TValue>)_impl).IsReadOnly;

        public TValue this[TKey key]
        {
            get => _impl[key];
            set => _impl[key] = value;
        }

        public ICollection<TKey>   Keys   => _impl.Keys;
        public ICollection<TValue> Values => _impl.Values;

        /// <summary>
        /// 原始的字典。
        /// </summary>
        public Dictionary<TKey, TValue> OriginalDictionary => _impl;
        public HashSet<TKey> OriginalHashSet => _set;
    }
}